// https://en.wikipedia.org/wiki/TIFF#TIFF_Compression_Tag
// https://exiftool.org/TagNames/EXIF.html#Compression
// https://github.com/image-rs/image-tiff/blob/master/src/decoder/mod.rs

// TODO support jpeg
// TODO decide on miniz_oxide vs flate2

use std::io::{self, Read, Write};
// use miniz_oxide::inflate::{self,TINFLStatus};
use num_enum::{FromPrimitive, IntoPrimitive};
use salzweg::decoder::{DecodingError, TiffStyleDecoder};
use salzweg::encoder::{EncodingError, TiffStyleEncoder};
use flate2;
use crate::raster:: SampleFormat;
use crate::tiff::Endian;
use super::bytecast;

#[derive(Debug)]
pub enum DecompressError {
    LzwDecodeError(DecodingError),
    LzwEncodeError(EncodingError),
    // InflateError(TINFLStatus),
    CompressionNotSupported(Compression),
    PredictorNotSupported(Predictor),
    IoError(io::Error),
}

impl From<io::Error> for DecompressError {
    fn from(e: io::Error) -> Self {
        DecompressError::IoError(e)
    }
}

#[derive(Debug, PartialEq, Clone, Copy, IntoPrimitive, FromPrimitive)]
#[repr(u16)]
pub enum Compression {
    Uncompressed = 1,
    CCITT1D = 2,
    T4Group3Fax = 3,
    T6Group4Fax = 4,
    Lzw = 5,
    JpegOld = 6,
    Jpeg = 7,
    DeflateAdobe = 8,
    JbigBW = 9,
    JbigColor = 10,
    JPEGOther = 99,
    Kodak262 = 262,
    Next = 32766,
    SonyARWCompressed = 32767,
    PackedRAW = 32769,
    SamsungSRWCompressed = 32770,
    CCIRLEW = 32771,
    SamsungSRWCompressed2 = 32772,
    PackBits = 32773,
    Thunderscan = 32809,
    KodakKDCCompressed = 32867,
    IT8CTPAD = 32895,
    IT8LW = 32896,
    IT8MP = 32897,
    IT8BL = 32898,
    PixarFilm = 32908,
    PixarLog = 32909,
    Deflate = 32946,
    DCS = 32947,
    AperioJPEG2000YCbCr = 33003,
    AperioJPEG2000RGB = 33005,
    JBIG = 34661,
    SGILog = 34676,
    SGILog24 = 34677,
    JPEG2000 = 34712,
    NikonNEFCompressed = 34713,
    JBIG2TIFFFX = 34715,
    MdiBinaryLevelCodec = 34718,
    MdiProgressiveTransformCodec = 34719,
    MdiVector = 34720,
    ESRILerc = 34887,
    LossyJPEG = 34892,
    LZMA2 = 34925,
    Zstd = 34926,
    WebP = 34927,
    PNG = 34933,
    JPEGXR = 34934,
    JPEGXL = 52546,
    KodakDCRCompressed = 65000,
    PentaxPEFCompressed = 65535,

    #[num_enum(default)]
    Unknown = 0x0000,
}

impl Compression {
    pub fn decode(&self, bytes: &[u8]) -> Result<Vec<u8>, DecompressError> {
        match self {
            Self::Uncompressed => Ok(bytes.to_vec()),
            Self::Lzw => {
                TiffStyleDecoder::decode_to_vec(bytes).map_err(|e| DecompressError::LzwDecodeError(e))
            }
            Self::DeflateAdobe => {
                let mut buf = vec![];
                flate2::read::ZlibDecoder::new(bytes).read_to_end(&mut buf)?;
                Ok(buf)
                // inflate::decompress_to_vec_zlib(bytes).map_err(|e| DecompressError::InflateError(e.status))
            }
            other => Err(DecompressError::CompressionNotSupported(*other)),
        }
    }

    pub fn encode(&self, bytes: &[u8]) -> Result<Vec<u8>, DecompressError> {
        match self {
            Self::Uncompressed => Ok(bytes.to_vec()),
            Self::Lzw => {
                TiffStyleEncoder::encode_to_vec(bytes).map_err(|e| DecompressError::LzwEncodeError(e))
            }
            Self::DeflateAdobe => {
                let mut encoder = flate2::write::ZlibEncoder::new(Vec::new(), flate2::Compression::default());
                encoder.write_all(bytes)?;
                Ok(encoder.finish()?)
                // inflate::decompress_to_vec_zlib(bytes).map_err(|e| DecompressError::InflateError(e.status))
            }
            other => Err(DecompressError::CompressionNotSupported(*other)),
        }
    }
}

#[derive(Debug, PartialEq, Clone, Copy, IntoPrimitive, FromPrimitive)]
#[repr(u16)]
pub enum Predictor {
    No = 1,
    Horizontal = 2,
    FloatingPoint = 3,

    #[num_enum(default)]
    Unknown = 0x0000,
}



trait Wrapping {
    fn wrapping_add(&self, other: Self) -> Self;
}

impl Wrapping for u8 {
    fn wrapping_add(&self, other: Self) -> Self {
        u8::wrapping_add(*self, other)
    }
}

impl Wrapping for u16 {
    fn wrapping_add(&self, other: Self) -> Self {
        u16::wrapping_add(*self, other)
    }
}

impl Wrapping for u32 {
    fn wrapping_add(&self, other: Self) -> Self {
        u32::wrapping_add(*self, other)
    }
}

impl Wrapping for u64 {
    fn wrapping_add(&self, other: Self) -> Self {
        u64::wrapping_add(*self, other)
    }
}

impl Wrapping for u128 {
    fn wrapping_add(&self, other: Self) -> Self {
        u128::wrapping_add(*self, other)
    }
}

impl Wrapping for i8 {
    fn wrapping_add(&self, other: Self) -> Self {
        i8::wrapping_add(*self, other)
    }
}

impl Wrapping for i16 {
    fn wrapping_add(&self, other: Self) -> Self {
        i16::wrapping_add(*self, other)
    }
}

impl Wrapping for i32 {
    fn wrapping_add(&self, other: Self) -> Self {
        i32::wrapping_add(*self, other)
    }
}

impl Wrapping for i64 {
    fn wrapping_add(&self, other: Self) -> Self {
        i64::wrapping_add(*self, other)
    }
}

impl Wrapping for i128 {
    fn wrapping_add(&self, other: Self) -> Self {
        i128::wrapping_add(*self, other)
    }
}

fn rev_hpredict_nsamp<T: Copy + Wrapping>(image: &mut [T], samples: usize) {
    for col in samples..image.len() {
        image[col] = image[col].wrapping_add(image[col - samples]);
    }
}

pub fn fp_predict_f32(input: &mut [u8], output: &mut [f32], samples: usize) {
    rev_hpredict_nsamp(input, samples);
    for i in 0..output.len() {
        // TODO: use f32::from_be_bytes() when we can (version 1.40)
        output[i] = f32::from_be_bytes([
            input[i],
            input[input.len() / 4 + i],
            input[input.len() / 4 * 2 + i],
            input[input.len() / 4 * 3 + i],
        ]);
    }
}

pub fn fp_predict_f64(input: &mut [u8], output: &mut [f64], samples: usize) {
    rev_hpredict_nsamp(input, samples);
    for i in 0..output.len() {
        // TODO: use f64::from_be_bytes() when we can (version 1.40)
        output[i] = f64::from_bits(u64::from_be_bytes([
            input[i],
            input[input.len() / 8 + i],
            input[input.len() / 8 * 2 + i],
            input[input.len() / 8 * 3 + i],
            input[input.len() / 8 * 4 + i],
            input[input.len() / 8 * 5 + i],
            input[input.len() / 8 * 6 + i],
            input[input.len() / 8 * 7 + i],
        ]));
    }
}


impl Predictor {
    pub fn predict(
        &self,
        buffer: &mut [u8],
        width: usize,
        bit_depth: usize,
        samples_per_pixel: usize,
        sample_format: &Vec<SampleFormat>,
        endian: Endian,
    ) -> Result<(), DecompressError> {
        let row_len =  width * samples_per_pixel * bit_depth / 8;

        match self {
            Self::No => { buffer}
            Self::Horizontal => {
                match sample_format.first() {
                    Some(SampleFormat::Unsigned) => {
                        match bit_depth{
                            8 => {
                                let mut row_start = 0;
                                while row_start < buffer.len() {
                                    let row_end = row_start + row_len;
                                    rev_hpredict_nsamp(&mut buffer[row_start..row_end], samples_per_pixel);
                                    row_start = row_end;
                                }
                                buffer},
                            16 => {
                                let mut row_start = 0;
                                while row_start < buffer.len() {
                                    let row_end = row_start + row_len;
                                    let mut vec_buf = endian.decode_all_to_primative::<2, u16, u16>(&buffer[row_start..row_end]).unwrap_or_default();
                                    rev_hpredict_nsamp(vec_buf.as_mut_slice(), samples_per_pixel);
                                    let vec_buf = bytecast::u16_as_ne_mut_bytes(vec_buf.as_mut_slice());
                                    buffer[row_start..row_end].copy_from_slice(&vec_buf);
                                    row_start = row_end;
                                }
                                buffer
                            },
                            32 => {
                                let mut row_start = 0;
                                while row_start < buffer.len() {
                                    let row_end = row_start + row_len;
                                    let mut vec_buf = endian.decode_all_to_primative::<4, u32, u32>(&buffer[row_start..row_end]).unwrap();
                                    rev_hpredict_nsamp(vec_buf.as_mut_slice(), samples_per_pixel);
                                    let vec_buf = bytecast::u32_as_ne_mut_bytes(vec_buf.as_mut_slice());
                                    buffer[row_start..row_end].copy_from_slice(&vec_buf);
                                    row_start = row_end;
                                }
                                buffer
                            },
                            64 => {
                                let mut row_start = 0;
                                while row_start < buffer.len() {
                                    let row_end = row_start + row_len;
                                    let mut vec_buf = endian.decode_all_to_primative::<8, u64, u64>(&buffer[row_start..row_end]).unwrap();
                                    rev_hpredict_nsamp(vec_buf.as_mut_slice(), samples_per_pixel);
                                    let vec_buf = bytecast::u64_as_ne_mut_bytes(vec_buf.as_mut_slice());
                                    buffer[row_start..row_end].copy_from_slice(&vec_buf);
                                    row_start = row_end;
                                }
                                buffer
                            }
                            128 => {
                                let mut row_start = 0;
                                while row_start < buffer.len() {
                                    let row_end = row_start + row_len;
                                    let mut vec_buf = endian.decode_all_to_primative::<16, u128, u128>(&buffer[row_start..row_end]).unwrap();
                                    rev_hpredict_nsamp(vec_buf.as_mut_slice(), samples_per_pixel);
                                    let vec_buf = bytecast::u128_as_ne_mut_bytes(vec_buf.as_mut_slice());
                                    buffer[row_start..row_end].copy_from_slice(&vec_buf);
                                    row_start = row_end;
                                }
                                buffer
                            },
                            _ => return Err(DecompressError::PredictorNotSupported(Self::Horizontal))
                        }
                    },
                    Some(SampleFormat::Signed) => {
                        match bit_depth{
                            8 => {
                                let mut row_start = 0;
                                while row_start < buffer.len() {
                                    let row_end = row_start + row_len;
                                    rev_hpredict_nsamp(&mut buffer[row_start..row_end], samples_per_pixel);
                                    row_start = row_end;
                                }
                                buffer},
                            16 => {
                                let mut row_start = 0;
                                while row_start < buffer.len() {
                                    let row_end = row_start + row_len;
                                    let mut vec_buf = endian.decode_all_to_primative::<2, i16, i16>(&buffer[row_start..row_end]).unwrap();
                                    rev_hpredict_nsamp(vec_buf.as_mut_slice(), samples_per_pixel);
                                    let vec_buf =bytecast::i16_as_ne_mut_bytes(vec_buf.as_mut_slice());
                                    buffer[row_start..row_end].copy_from_slice(&vec_buf);
                                    row_start = row_end;
                                }
                                buffer
                            },
                            32 => {
                                let mut row_start = 0;
                                while row_start < buffer.len() {
                                    let row_end = row_start + row_len;
                                    let mut vec_buf = endian.decode_all_to_primative::<4, i32, i32>(&buffer[row_start..row_end]).unwrap();
                                    rev_hpredict_nsamp(vec_buf.as_mut_slice(), samples_per_pixel);
                                    let vec_buf =bytecast::i32_as_ne_mut_bytes(vec_buf.as_mut_slice());
                                    buffer[row_start..row_end].copy_from_slice(&vec_buf);
                                    row_start = row_end;
                                }
                                buffer
                            },
                            64 => {
                                let mut row_start = 0;
                                while row_start < buffer.len() {
                                    let row_end = row_start + row_len;
                                    let mut vec_buf = endian.decode_all_to_primative::<8, i64, i64>(&buffer[row_start..row_end]).unwrap();
                                    rev_hpredict_nsamp(vec_buf.as_mut_slice(), samples_per_pixel);
                                    let vec_buf =bytecast::i64_as_ne_mut_bytes(vec_buf.as_mut_slice());
                                    buffer[row_start..row_end].copy_from_slice(&vec_buf);
                                    row_start = row_end;
                                }
                                buffer
                            }
                            128 => {
                                let mut row_start = 0;
                                while row_start < buffer.len() {
                                    let row_end = row_start + row_len;
                                    let mut vec_buf = endian.decode_all_to_primative::<16, i128, i128>(&buffer[row_start..row_end]).unwrap();
                                    rev_hpredict_nsamp(vec_buf.as_mut_slice(), samples_per_pixel);
                                    let vec_buf =bytecast::i128_as_ne_mut_bytes(vec_buf.as_mut_slice());
                                    buffer[row_start..row_end].copy_from_slice(&vec_buf);
                                    row_start = row_end;
                                }
                                buffer
                            },
                            _ => return Err(DecompressError::PredictorNotSupported(Self::Horizontal))
                        }
                    },
                    _ => return Err(DecompressError::PredictorNotSupported(Self::Horizontal))

                }
            },
            Self::FloatingPoint => {
                match sample_format.first() {
                    Some(SampleFormat::Float) => {
                        match bit_depth{
                            32 => {
                                let mut row_start = 0;
                                let mut vec_buf = vec![0.0; row_len / 4];
                                while row_start < buffer.len() {
                                    let row_end = row_start + row_len;
                                    fp_predict_f32(&mut buffer[row_start..row_end], &mut vec_buf, samples_per_pixel);
                                    let vec_buf = bytecast::f32_as_ne_mut_bytes(&mut vec_buf);
                                    buffer[row_start..row_end].copy_from_slice(&vec_buf);
                                    row_start = row_end;
                                }
                                buffer
                            },
                            64 => {
                                let mut row_start = 0;
                                let mut vec_buf = vec![0.0; row_len / 8];
                                while row_start < buffer.len() {
                                    let row_end = row_start + row_len;
                                    fp_predict_f64(&mut buffer[row_start..row_end], &mut vec_buf, samples_per_pixel);
                                    let vec_buf = bytecast::f64_as_ne_mut_bytes(&mut vec_buf);
                                    buffer[row_start..row_end].copy_from_slice(&vec_buf);
                                    row_start = row_end;
                                    }
                                buffer
                                },
                            _ => return Err(DecompressError::PredictorNotSupported(Self::FloatingPoint))
                        }
                    },
                    _ => return Err(DecompressError::PredictorNotSupported(Self::FloatingPoint))
                    
                }
            }
            other => return Err(DecompressError::PredictorNotSupported(*other)),
        };
        Ok(())
    }
}
